# 创建一个物理单体模拟系统
# 矢量场坐标方向:向右为x方向的正方向,向下为y方向的正方向
# author: Liam Wu
# beginDate: 2025/9/30

import pygame
import config
import math
from pygame import gfxdraw
import pymunk
from pymunk.pygame_util import DrawOptions
import random
import sys
import pygame_gui
import collections
import test_
# 颜色设置区域
SourceColor = (255,255,255)
ParticleColor = (200,255,200)
Purple = (110,10,150)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
BLUE = (0, 120, 255)
SKY_BLUE = (120, 180, 255)
WIDTH = 840
HEIGHT = 520
clock = pygame.time.Clock()
pygame.init()
pygame.display.set_caption("物理单体模拟系统")
screen = pygame.display.set_mode((WIDTH,HEIGHT))
# 创建pymunk物理空间
space = pymunk.Space()
space.gravity = (0,0)
# 添加pymunk的绘制控件
draw_options = DrawOptions(screen)
# 创建一个manager管家
manager = pygame_gui.UIManager((WIDTH,HEIGHT),"theme.json")
text_input = None
slider = None
button_SetPos = None
button_copyElectric = None
button_deleteElectric = None
button_toggle_field_lines = None
button_createElectric = None
# 创建电场源面板的UI组件
create_text_input_x = None
create_text_input_y = None
create_slider = None
button_confirm_create = None
ParticleDataSlideToHide = False
ParticleDataSlideToShow = False
showCreateElectric = False
# 将所有pygame_gui控件放入一个列表内
gui_arr = [text_input,slider,button_SetPos,button_createElectric,button_deleteElectric,button_copyElectric]
# 弹框状态
popup_visible = False

def destroy_all_gui_components():
    """销毁所有pygame_gui组件"""
    global text_input, slider, button_SetPos, button_copyElectric, button_deleteElectric
    global button_createElectric, create_text_input_x, create_text_input_y, create_slider, button_confirm_create
    
    # 销毁主要的GUI组件
    if text_input is not None:
        text_input.kill()
        text_input = None
    if slider is not None:
        slider.kill()
        slider = None
    if button_SetPos is not None:
        button_SetPos.kill()
        button_SetPos = None
    if button_copyElectric is not None:
        button_copyElectric.kill()
        button_copyElectric = None
    if button_deleteElectric is not None:
        button_deleteElectric.kill()
        button_deleteElectric = None
    if button_createElectric is not None:
        button_createElectric.kill()
        button_createElectric = None
    
    # 销毁创建面板的GUI组件
    if create_text_input_x is not None:
        create_text_input_x.kill()
        create_text_input_x = None
    if create_text_input_y is not None:
        create_text_input_y.kill()
        create_text_input_y = None
    if create_slider is not None:
        create_slider.kill()
        create_slider = None
    if button_confirm_create is not None:
        button_confirm_create.kill()
        button_confirm_create = None

def cancel_introduce():
    message_system.add_message("关闭程序介绍页面",(100, 255, 100))
    global popup_visible
    popup_visible = False
# 创建一个介绍本程序的函数
def introduce(pos):
    overlay = pygame.Surface((WIDTH,HEIGHT))
    overlay.fill(BLACK)
    pygame.draw.rect(overlay, SKY_BLUE, (WIDTH/2-280,HEIGHT/2-180,500,350))
    but_cancel_introduce.imgDraw(pos,overlay)
    screen.blit(overlay, (0,0))
# 创建一个输入框函数
def textEntry(x,y,width,height):
    text_rect = pygame.Rect(x,y,width,height)
    return pygame_gui.elements.UITextEntryLine(relative_rect=text_rect,manager=manager)
# 点击事件触发函数，当点击鼠标时进入滑动状态
def slide_right():
    global ParticleDataSlideToHide
    ParticleDataSlideToHide = True
def slide_left():
    global ParticleDataSlideToShow
    ParticleDataSlideToShow = True
def field_create():
    global showCreateElectric
    showCreateElectric = not showCreateElectric
    if showCreateElectric:
        CreateBox.start_animation()
def toggle_field_lines():
    """切换电场线显示状态"""
    global field_lines_system
    if field_lines_system is not None:
        field_lines_system.show_field_lines = not field_lines_system.show_field_lines
        # print(f"电场线显示状态: {'显示' if field_lines_system.show_field_lines else '隐藏'}")
    else:
        message_system.add_message("电场线系统未初始化！", (255, 100, 100))
# 创建一个按钮函数
def buttons(x,y,width,height,texts,hover_text):
     return pygame_gui.elements.UIButton(
        relative_rect=pygame.Rect(x + 118, y, width, height),
        text=texts,
        manager=manager,
        tool_tip_text=hover_text,  # 鼠标悬停提示
        object_id='#my_btn'  # 方便在主题文件里单独写样式
    )
class Animation_PartData:
    def __init__(self,x,y):
        self.x = x
        self.y = y
        self.vx = 0
        self.ax = 1
        self.width = 180
        self.height = 80
        self.original_x = x
        self.hide_state = False
    def reset(self):
        self.x = self.original_x
    def update(self):
        # 播放向右动画
        global ParticleDataSlideToHide
        if self.x < WIDTH:
            self.vx += self.ax
            self.x += self.vx
        else:
            # 当动画结束的时候设置区域的隐藏状态和滑动状态
            self.vx = 0
            self.hide_state = True
            ParticleDataSlideToHide = False
    def update_left(self):
        # 播放向左动画
        global ParticleDataSlideToShow
        if self.x > self.original_x:
            self.vx -= self.ax
            self.x += self.vx
        else:
            # 当动画结束的时候设置区域的隐藏状态和滑动状态
            self.vx = 0
            self.hide_state = False
            ParticleDataSlideToShow = False
    def draw(self,equal):
        pygame.draw.rect(screen, WHITE, (self.x, self.y, self.width, self.height), 2, 4)
        particle_text_nor.displayText(self.x + 5, self.y + 5, f"粒子运动参数", BLUE)
        particle_text_sm.displayText(self.x + 10, self.y + 25, f"粒子位置:({body.position.x:.1f},{body.position.y:.1f})")
        particle_text_sm.displayText(self.x + 10, self.y + 40, f"粒子速度:({body.velocity.x:.1f},{body.velocity.y:.1f})")
        text = "正"
        if equal < 0:
            text = "负"
        particle_text_sm.displayText(self.x + 10, self.y + 55, f"电性：{text}电荷   电荷量：{equal}C")
# 创建一个动画类,使程序看起来流畅
class Animation:
    def __init__(self,x,y):
        self.x = x
        self.y = y
        self.original_x = x  # 保存初始位置
    
    def reset(self):
        """重置动画位置，准备重新开始"""
        self.x = self.original_x
    
    def update(self):
        if self.x > 650:
            self.x -= 4
    def draw(self, x, y, equal, surface, position):
        pygame.draw.rect(screen, WHITE, (self.x, self.y, 180, 345), 2, 10)
        # 绘制电场源的电场强度数据
        text_ch.displayText(self.x+50,self.y+15,f"{index+1}号电场源",BLUE)
        text_sm.displayText(self.x+20,self.y+45,"1.电荷量")
        text_sm.displayText(self.x+35,self.y+65,f"Q = {equal} C")
        text_sm.displayText(self.x+18,self.y+85,"2.电性")
        charge = "正"
        if equal<0:
            charge = "负"
        text_sm.displayText(self.x+35,self.y+105,charge+"电荷")
        text_sm.displayText(self.x+18,self.y+125,"3.位置坐标")
        text_sm.displayText(self.x + 35, self.y + 145, f"({x},{y})")
        global text_input
        global slider
        global button_SetPos
        global button_copyElectric
        global button_deleteElectric
        if text_input is None:
            text_input = textEntry(self.x+35,self.y+165,80,25)
        else:
            # 动画平移时，实时更新输入框位置
            text_input.set_relative_position((self.x+35, self.y+165))
        if slider is None:
            slider = pygame_gui.elements.UIHorizontalSlider(
            relative_rect=pygame.Rect(self.x+85, self.y+60, 90, 25),
            start_value=equal,
            value_range=(equal-45, equal+45),
            manager=manager,
            click_increment=1  # 点击两侧箭头时的步长
            )
        else:
            slider.set_relative_position((self.x+85, self.y+60))
        if button_SetPos is None:
            button_SetPos = buttons(self.x + 118,199,40,26,"确定",'change the electric field position')
        else:
            button_SetPos.set_relative_position((self.x+118,199))
        if button_copyElectric is None:
            button_copyElectric = buttons(self.x+95,330,40,26,"复制","copy a electric field source")
        else:
            button_copyElectric.set_relative_position((self.x+95,330))
        if button_deleteElectric is None:
            button_deleteElectric = buttons(self.x+50,330,40,26,"删除","delete a electric field source")
        else:
            button_deleteElectric.set_relative_position((self.x+50,330))
        # ButtonDelete = Button(self.x+60, 340, 60, 20, WHITE, 16, RED, 'img', "删除",on_test_click,"font/font.ttf")
        # ButtonDelete.normalDraw(position)
        ButtonDelete = None
        return [ButtonDelete]
def on_set_click():
    try:
        text = text_input.get_text()
        vis = [int(x) for x in text[1:][:-1].split(",")]
        config.SourcePosArr[index][0] = vis[0]
        config.SourcePosArr[index][1] = vis[1]
        # 同步更新arr里的对象坐标
        arr[index].x = vis[0]
        arr[index].y = vis[1]
        arr[index].rect.x = vis[0] - 25
        arr[index].rect.y = vis[1] - 25
        
        # 更新电场线
        if field_lines_system is not None:
            field_lines_system.sources = arr
            field_lines_system.calculate_field_lines()
    except ValueError:
        message_system.add_message("数据输入错误，请重新输入！", (255, 100, 100))

def on_confirm_create_click():
    """处理确认创建电场源按钮的点击事件"""
    global arr, field_lines_system, showCreateElectric
    try:
        # 获取输入框的值
        x_text = create_text_input_x.get_text()
        y_text = create_text_input_y.get_text()
        
        # 解析坐标
        x = int(x_text) if x_text else 400  # 默认值
        y = int(y_text) if y_text else 300  # 默认值
        
        # 获取滑块的值
        charge = create_slider.get_current_value()
        
        # 创建新的电场源
        new_source = [x, y, charge]
        config.SourcePosArr.append(new_source)
        
        # 重新创建电场源数组
        arr = createElectricSourceArrB(config.SourcePosArr)
        
        # 重新计算电场线
        if field_lines_system is not None:
            field_lines_system.sources = arr
            field_lines_system.calculate_field_lines()
        
        # 隐藏创建面板
        showCreateElectric = False
        
        message_system.add_message(f"成功创建电场源: 位置({x}, {y}), 电荷量{charge}", (100, 255, 100))
        
    except ValueError:
        message_system.add_message("输入数据格式错误，请检查坐标输入！", (255, 100, 100))
    except Exception as e:
        message_system.add_message(f"创建电场源时发生错误: {e}", (255, 100, 100))

# 消息显示系统类
class MessageSystem:
    def __init__(self):
        self.messages = []
        self.max_messages = 5  # 最多显示5条消息
        self.message_duration = 3000  # 消息显示时间（毫秒）
        self.font = pygame.font.Font("font/font.ttf", 14)
    
    def add_message(self, text, color=(255, 255, 255)):
        """添加一条消息"""
        current_time = pygame.time.get_ticks()
        self.messages.append({
            'text': text,
            'color': color,
            'start_time': current_time
        })
        # 保持消息数量不超过最大值
        if len(self.messages) > self.max_messages:
            self.messages.pop(0)
    
    def update(self):
        """更新消息系统，移除过期的消息"""
        current_time = pygame.time.get_ticks()
        self.messages = [msg for msg in self.messages 
                        if current_time - msg['start_time'] < self.message_duration]
    
    def draw(self, screen):
        """在右上角绘制消息"""
        self.update()
        start_x = WIDTH - 300  # 右上角起始位置
        start_y = 10
        
        for i, msg in enumerate(self.messages):
            # 计算透明度（消息即将消失时变淡）
            current_time = pygame.time.get_ticks()
            elapsed = current_time - msg['start_time']
            alpha = max(0, 255 - int((elapsed / self.message_duration) * 255))
            
            # 创建带透明度的颜色
            color = (*msg['color'], alpha)
            
            # 绘制消息背景
            text_surface = self.font.render(msg['text'], True, msg['color'])
            bg_rect = pygame.Rect(start_x - 5, start_y + i * 25 - 2, 
                                text_surface.get_width() + 10, text_surface.get_height() + 4)
            pygame.draw.rect(screen, (0, 0, 0, alpha), bg_rect)
            pygame.draw.rect(screen, (100, 100, 100, alpha), bg_rect, 1)
            
            # 绘制消息文字
            screen.blit(text_surface, (start_x, start_y + i * 25))

# 创建全局消息系统实例
message_system = MessageSystem()

# 创建一个文字类 便于在界面上显示文字
# 文字类的字体文件打包时容易出现问题，需要注意!!!
class Text:
    def __init__(self, fontSize=25,fontPath=None,text = None):
        """
        初始化文字类
        :param fontSize: 文字的字体大小
        :param fontPath: 文字的字体路径(默认None,使用系统字体)
        """
        self.fontSize = fontSize
        self.fontPath = fontPath
        self.font = pygame.font.Font(self.fontPath,self.fontSize)
        self.text = None
    def displayText(self,x,y,text,color = WHITE):
        """
        绘制文字
        :param x: 绘制字体的x坐标
        :param y: 绘制字体的y坐标
        :param text: 绘制的text文本
        :param color: 绘制字体的颜色
        :return: 无
        """
        self.text = text
        textSurface = self.font.render(self.text,True,color)
        screen.blit(textSurface,(x,y))



text_ch = Text(18,"font/font.ttf")
text_sm = Text(14,"font/font.ttf")
particle_text_nor = Text(16,"font/font.ttf")
particle_text_sm = Text(14,"font/font.ttf")
# 创建一个按钮类,便于绘制按钮
class Button:
    def __init__(self,x,y,width,height,bgColor,fontSize,hoverColor,img,img_blue,callback,text=None,fontPath = None):
        """
        :param x: 按钮左上角点的x坐标
        :param y: 按钮左上角点的y坐标
        :param width: 按钮的宽度
        :param height: 按钮的高度
        :param bgColor: 按钮的背景颜色
        :param fontSize: 按钮中字体的大小
        :param hoverColor: 当鼠标滑上按钮时按钮的背景颜色会发生改变
        :param img: 某些按钮可能需要图片作为背景，img的值为图片的相对路径地址
        :param text: 按钮中的字体内容
        :param fontPath: 字体文件的文件路径
        """
        self.rect = pygame.Rect(x,y,width,height)
        self.saveRect = pygame.Rect(x,y,width,height)
        self.bgColor = bgColor
        self.fontSize = fontSize
        self.hoverColor = hoverColor
        self.img = pygame.image.load(img).convert_alpha()
        self.img_blue = pygame.image.load(img_blue).convert_alpha()
        self.text = text
        self.isHovered = False
        self.fontPath = fontPath
        self.callback = callback
        self.font = pygame.font.Font(self.fontPath, self.fontSize)
    # 普通绘制一下按钮
    def normalDraw(self, position):
        # 形式参数position为鼠标在屏幕上的位置,传递路径较长
        # 绘制按钮边框
        pygame.draw.rect(screen, BLUE, (self.rect.x-2, self.rect.y-2, self.rect.width+4, self.rect.height+4), 2, 5)
        # 计算文字中心以便使文字在按钮中居中显示
        center_x = self.rect.x + self.rect.width / 2
        center_y = self.rect.y + self.rect.height / 2
        # 字体的相关的设置
        textSurface = self.font.render(self.text, True, BLUE)
        text_rect = textSurface.get_rect()
        # 绘制按钮背景颜色
        if self.rect.collidepoint(position):
            pygame.draw.rect(screen, SKY_BLUE, self.rect, 0, 3)
            self.isHovered = True
        else:
            pygame.draw.rect(screen, self.bgColor, self.rect, 0, 3)
            self.isHovered = False
        # 只负责绘制和hover，不处理回调
        if text_rect:
            text_rect.center = (int(center_x), int(center_y))
        screen.blit(textSurface, text_rect)
    def imgDraw(self,position,surface):
        # 这个是当鼠标滑上按钮之后的切换箭头为蓝色，增强用户体验
        if self.rect.collidepoint(position):
            img_blue = pygame.transform.scale(self.img_blue, (self.rect.w, self.rect.h))
            surface.blit(img_blue, (self.rect.x+5, self.rect.y))
            self.isHovered = True
        else:
            img = pygame.transform.scale(self.img, (self.rect.w, self.rect.h))
            surface.blit(img, (self.rect.x, self.rect.y))
            self.isHovered = True
        self.isHovered = False

but_right = Button(620,420,25,25,WHITE,14,RED,"img/right.png","img/right_blue.png",slide_right)
but_left = Button(WIDTH-25,420,25,25,WHITE,14,RED,"img/left.png","img/left_blue.png",slide_left)
but_cancel_introduce = Button(640,50,25,25,WIDTH,14,RED,"img/cancel.png","img/cancel_blue.png",cancel_introduce)
# 创建一个简单的电场线按钮类
class FieldLinesButton:
    def __init__(self, x, y, width, height, text, callback):
        self.rect = pygame.Rect(x, y, width, height)
        self.text = text
        self.callback = callback
        self.font = pygame.font.Font("font/font.ttf", 14)
        self.isHovered = False
    
    def normalDraw(self, position):
        # 绘制按钮边框
        pygame.draw.rect(screen, BLUE, (self.rect.x-2, self.rect.y-2, self.rect.width+4, self.rect.height+4), 2, 5)
        
        # 计算文字中心
        center_x = self.rect.x + self.rect.width / 2
        center_y = self.rect.y + self.rect.height / 2
        
        # 绘制按钮背景
        if self.rect.collidepoint(position):
            pygame.draw.rect(screen, SKY_BLUE, self.rect, 0, 3)
            self.isHovered = True
        else:
            pygame.draw.rect(screen, WHITE, self.rect, 0, 3)
            self.isHovered = False
        
        # 绘制文字
        textSurface = self.font.render(self.text, True, BLUE)
        text_rect = textSurface.get_rect()
        text_rect.center = (int(center_x), int(center_y))
        screen.blit(textSurface, text_rect)

but_field_lines = FieldLinesButton(WIDTH-90, HEIGHT-30, 70, 20, "电场线绘制", toggle_field_lines)
but_field_create = FieldLinesButton(WIDTH-175,HEIGHT-30,70,20,"创建电场源",field_create)
# 性能模式切换按钮
def toggle_performance_mode():
    """切换性能模式"""
    global performance_settings
    if performance_settings.field_line_density == 8:  # 当前是高性能模式
        performance_settings.set_balanced_performance()
        message_system.add_message("性能模式: 平衡模式 (中等质量)", (100, 255, 100))
    elif performance_settings.field_line_density == 12:  # 当前是平衡模式
        performance_settings.set_high_quality()
        message_system.add_message("性能模式: 高质量模式 (最佳视觉效果)", (100, 255, 100))
    else:  # 当前是高质量模式
        performance_settings.set_high_performance()
        message_system.add_message("性能模式: 高性能模式 (最低CPU负载)", (100, 255, 100))
    
    # 更新电场线系统设置
    if field_lines_system is not None:
        field_lines_system.line_density = performance_settings.field_line_density
        field_lines_system.calculation_interval = performance_settings.field_line_update_interval
        field_lines_system.calculate_field_lines()

but_performance = FieldLinesButton(WIDTH-270, HEIGHT-30, 80, 20, "性能模式", toggle_performance_mode)
# 创建一个电场源
class ElectricSource:
    def __init__(self, x, y, equal):
        """
        初始化电场源
        :param x: 电场源的x坐标
        :param y: 电场源的y坐标
        :param equal: 电场源的强度(含正负)
        """
        self.x = x
        self.y = y
        self.equal = equal
        self.rect = pygame.Rect(x-25,y-25,50,50)
        self.Rects = pygame.Rect(650,35,180,345)
        self.isHovered = False
        self.is_deleting = False
        self.delete_progress = 0  # 0~1
        self.explode_particles = []  # 爆炸粒子列表
        self.imageP = pygame.image.load("img/Pcharge.png").convert_alpha()
        self.imageN = pygame.image.load("img/Ncharge.png").convert_alpha()
    def getElectricField(self, x, y):
        dx = x - self.x
        dy = y - self.y
        distance = math.hypot(dx, dy)
        min_dist = 1e-2  # 防止距离过小导致数值爆炸
        if distance < min_dist:
            distance = min_dist
        unit_x = dx / distance
        unit_y = dy / distance
        eStrength = config.k * self.equal / (distance ** 2)
        E_x = eStrength * unit_x
        E_y = eStrength * unit_y
        return [E_x, E_y]

    def start_delete(self):
        """
        开始删除动画
        :return: 无
        """
        self.is_deleting = True
        self.delete_progress = 0
        # 存储爆炸粒子的列表
        self.explode_particles = []
        # 生成爆炸粒子
        num_particles = 18
        for i in range(num_particles):
            # 计算粒子初始速度和方向，生成随机速度和角度
            angle = 2 * math.pi * i / num_particles
            speed = random.uniform(2, 5)
            # 计算粒子的速度分量
            vx = math.cos(angle) * speed
            vy = math.sin(angle) * speed
            # 保存进粒子列表
            self.explode_particles.append({
                "x": self.x,
                "y": self.y,
                "vx": vx,
                "vy": vy,
                "alpha": 255
            })

    def disPlayBall(self):
        """
        绘制场源电荷样式
        :return: 无
        """
        # 动画缩小+爆炸
        if self.is_deleting:
            self.delete_progress += 0.08
            scale = max(0, 1 - self.delete_progress * 1.5)
        else:
            scale = 1
        radius = int(10 * scale)
        if radius > 0 and not self.is_deleting:
            pygame.draw.circle(screen, SourceColor, (self.x, self.y), radius)
            pygame.gfxdraw.circle(screen, self.x, self.y, int(20 * scale), SourceColor)
        # 绘制爆炸粒子
        if self.is_deleting and self.explode_particles:
            for p in self.explode_particles:
                # 粒子运动
                p["x"] += p["vx"]
                p["y"] += p["vy"]
                p["alpha"] = max(0, p["alpha"] - 12)
                color = (255, 255, 100, int(p["alpha"]))
                s = pygame.Surface((8, 8), pygame.SRCALPHA)
                pygame.draw.circle(s, color, (4, 4), 2)
                screen.blit(s, (p["x"] - 4, p["y"] - 4))
        # 绘制电荷的正负标识
        if not self.is_deleting:
            # ...existing code for charge icon...
            if self.equal > 0:
                new_img = pygame.transform.scale(self.imageP,(25,25))
                screen.blit(new_img,(self.x-12,self.y-12))
            else:
                new_img = pygame.transform.scale(self.imageN, (25, 25))
                screen.blit(new_img, (self.x - 12, self.y - 12))

    def JudgeHovered(self,pos):
        """
        判断是否选中了该电场源
        :param pos: 鼠标位置
        :return: 无
        """
        if self.rect.collidepoint(pos):
            self.isHovered = True
        else:
            self.isHovered = False
    def displayElectricFieldData(self,surface,animation_obj,position,x=700,y=35):
        """
        绘制电场源的电场强度数据
        :return:
        """
        global showCreateElectric
        pygame.draw.rect(screen,(110,10,150),self.rect,2,10)
        if self.isHovered:
            animation_obj.update()
            ButtonTest = animation_obj.draw(self.x,self.y,self.equal,surface,position)
            showCreateElectric = False
        return ButtonTest
ani_p = Animation_PartData(650,390)
# 建立电场系统，便于计算任意一个点的叠加场强
class ElectricFieldSystem:
    def __init__(self,x,y,SourceArr):
        """
        初始化一个电场系统
        :param x: 待求点的x横坐标
        :param y: 待求点的y纵坐标
        :param SourceArr: 长场源电荷的对象列表
        """
        self.x = x
        self.y = y
        self.SourceArr = SourceArr
    def countElectricField(self):
        """
        计算电场系统中的某一点电场强度
        :return: Ex和Ey组成的列表
        """
        E_x = 0
        E_y = 0
        for i in self.SourceArr:
            temp = i.getElectricField(self.x,self.y)
            E_x += temp[0]
            E_y += temp[1]
        return [E_x,E_y]
# 创建一个单体运动粒子类
class ParticleSystem:
    def __init__(self,equal,x,y):
        """
        初始化粒子系统
        :param equal: 电荷量
        :param x: 初始放置粒子时的x坐标
        :param y: 初始放置粒子时的y坐标
        """
        self.equal = equal
        self.x = x
        self.y = y

    def displayParticle(self):
        """
        绘制运动粒子样式
        :return: 无
        """
        global body
        body = pymunk.Body(1,pymunk.moment_for_circle(1, 0, 10))
        body.position = (self.x,self.y)
        shape = pymunk.Circle(body,8)
        space.add(body,shape)
        shape.color = (ParticleColor[0], ParticleColor[1], ParticleColor[2], 255)
    # 绘制立体小球尾部散装粒子,增强观赏性
    def drawTrajectory(self):
        """
        绘制小球尾部散装粒子，模拟喷气效果
        :return: 无
        """
        randomNum = random.randint(1,3)
        vec = [-body.velocity.x,-body.velocity.y]
        alpha = math.atan2(vec[1],vec[0])
        thetaL = alpha + (5/180) * math.pi
        thetaR = alpha - (5/180) * math.pi
        # 计算速度反方向的喷射向量
        vecP = [math.cos(thetaL),math.sin(thetaL)]
        vecM = [math.cos(thetaR),math.sin(thetaR)]
        # 创建粒子刚体,便于操作
        body_p = pymunk.Body(1,pymunk.moment_for_circle(1, 0, 10))
        body_p.position = (body.position.x,body.position.y)
        # 通过随机函数生成随机数使得喷射出的粒子在三个方向的概率相等
        if randomNum == 1:
            body_p.velocity = vecP
        elif randomNum == 2:
            body_p.velocity = vec
        else:
            body_p.velocity = vecM
        shape_p = pymunk.Circle(body_p,2)
        config.bodyAndShape.append((body_p,shape_p))
        space.add(body_p,shape_p)
        shape_p.color = (ParticleColor[0], ParticleColor[1], ParticleColor[2], 255)
    def displayParticleData(self,x,y,width,height):
        # 条件解释:在向右滑动状态就播放向右动画
        if ParticleDataSlideToHide:
            ani_p.update()
        # 条件解释:在向左滑动状态并在隐藏状态就播放向左动画
        if ani_p.hide_state and ParticleDataSlideToShow:
            ani_p.update_left()
        ani_p.draw(self.equal)


# 绘制电子所用,降低代码耦合度
def drawSimplify(centerX,centerY,radius,theta):
    """
    :param centerX: 所围绕场源电荷旋转的横坐标x
    :param centerY: 所围绕场源电荷旋转的纵坐标y
    :param radius: 所围绕场源电荷的轨迹半径
    :param theta: 旋转的初始相位
    :return: 无
    """
    x = centerX+ radius * math.cos(theta)
    y = centerY+ radius * math.sin(theta)
    pygame.draw.circle(screen,ParticleColor,(int(x),int(y)),4)
# 新建场源电荷的外层模拟电子绘制情况
def electron(centerX=210,centerY=200,radius = 25,agular = 0.05):
    """需要外部设置三个变量表示电子的初始相位"""
    global thetas
    global theta
    global thetat
    thetas += agular
    theta += agular
    thetat += agular
    # 绘制电子
    drawSimplify(centerX,centerY,radius,theta)
    drawSimplify(centerX,centerY,radius,thetas)
    drawSimplify(centerX,centerY,radius,thetat)
# 绘制场源电荷的所有样式和方法，用于优化代码重复调用
def drawSourceTotal(arr):
    """
    绘制场源电荷的所有样式和方法，用于优化代码重复调用
    :param arr: 场源电荷对象列表
    :return: 无
    """
    agular = 0.05
    n = len(arr)
    for i in arr:
        i.disPlayBall()
        electron(i.x,i.y,20,agular/n)
# 创建一个电场源对象数组（依据中心坐标和缩放比列进行创建）
def createElectricSourceArrA(x,y,scale,equal):
    ans = []
    nx = [1,1,1,0,0,0,-1,-1,-1]
    ny = [-1,0,1,-1,0,1,-1,0,1]
    eq = [1,1,1,1,1,1,-1,-1,-1]
    for i in range(9):
        vis = []
        if nx[i] == 1:
            vis.append(x+scale*2)
        elif nx[i] == 0:
            vis.append(x)
        elif nx[i] == -1:
            vis.append(x-scale*2)
        if ny[i] == 0:
            vis.append(y)
        elif ny[i] == 1:
            vis.append(y+scale*2)
        elif ny[i] == -1:
            vis.append(y-scale*2)
        vis.append(equal)
        ans.append(ElectricSource(vis[0],vis[1],eq[i]*10))
    return ans
# 创建一个电场源对象数组（依据已有场源电荷数组进行创建）
def createElectricSourceArrB(SourceArr):
    ans = []
    for i in SourceArr:
        ans.append(ElectricSource(i[0],i[1],i[2]))
    return ans
# 绘制小球的运动轨迹
def draw_smooth_trajectory(points, color=(255, 100, 100), width=1):
    if len(points) > 2:
        # 使用pygame.gfxdraw.aapolygon进行抗锯齿曲线绘制
        pts = [(int(x), int(y)) for x, y in points]
        try:
            import pygame.gfxdraw
            # for i in range(len(pts)-1):
            #     pygame.gfxdraw.line(screen, pts[i][0], pts[i][1], pts[i+1][0], pts[i+1][1], color)
            pygame.draw.lines(screen, color, False, pts, width)
        except Exception:
            pygame.draw.lines(screen, color, False, pts, width)

# 创建一个粒子
startX = 420
startY = 140
particle = ParticleSystem(20,startX,startY)
particle.displayParticle()
# 性能设置类
class PerformanceSettings:
    def __init__(self):
        self.field_line_density = 12  # 电场线密度 (5-20)
        self.field_line_update_interval = 1500  # 电场线更新间隔(毫秒)
        self.max_field_line_length = 200  # 最大电场线长度
        self.field_line_step_size = 2.5  # 电场线步长
        self.draw_arrows = True  # 是否绘制箭头
        self.arrow_spacing = 20  # 箭头间距
        self.target_fps = 60  # 目标帧率
        
    def set_high_performance(self):
        """设置为高性能模式"""
        self.field_line_density = 8
        self.field_line_update_interval = 3000
        self.max_field_line_length = 150
        self.field_line_step_size = 3
        self.draw_arrows = False
        self.arrow_spacing = 30
        self.target_fps = 60
        
    def set_balanced_performance(self):
        """设置为平衡模式"""
        self.field_line_density = 12
        self.field_line_update_interval = 1500
        self.max_field_line_length = 200
        self.field_line_step_size = 2.5
        self.draw_arrows = True
        self.arrow_spacing = 20
        self.target_fps = 60
        
    def set_high_quality(self):
        """设置为高质量模式"""
        self.field_line_density = 20
        self.field_line_update_interval = 500
        self.max_field_line_length = 300
        self.field_line_step_size = 1.5
        self.draw_arrows = True
        self.arrow_spacing = 15
        self.target_fps = 60

# 创建性能设置实例
performance_settings = PerformanceSettings()
performance_settings.set_balanced_performance()  # 默认使用平衡模式

# 在程序启动时显示初始性能模式
def show_initial_performance_mode():
    """显示初始性能模式"""
    message_system.add_message("性能模式: 平衡模式 (中等质量)", (100, 255, 100))

# 电场线系统类
class ElectricFieldLines:
    def __init__(self, sources):
        """
        初始化电场线系统
        :param sources: 电场源列表
        """
        self.sources = sources
        self.field_lines = []
        self.show_field_lines = False
        self.line_density = performance_settings.field_line_density
        self.last_calculation_time = 0
        self.calculation_interval = performance_settings.field_line_update_interval
        
    def calculate_field_lines(self):
        """
        计算电场线
        """
        self.field_lines = []
        
        for source in self.sources:
            if source.equal > 0:  # 正电荷
                self._calculate_positive_charge_lines(source)
            else:  # 负电荷
                self._calculate_negative_charge_lines(source)
        
        self.last_calculation_time = pygame.time.get_ticks()
    
    def should_recalculate(self):
        """
        检查是否需要重新计算电场线
        """
        current_time = pygame.time.get_ticks()
        return current_time - self.last_calculation_time > self.calculation_interval
    
    def _calculate_positive_charge_lines(self, source):
        """
        计算正电荷的电场线（从电荷向外）
        """
        num_lines = self.line_density
        for i in range(num_lines):
            angle = 2 * math.pi * i / num_lines
            line_points = self._trace_field_line(source.x, source.y, angle, outward=True)
            if line_points:
                self.field_lines.append(line_points)
    
    def _calculate_negative_charge_lines(self, source):
        """
        计算负电荷的电场线（向电荷汇聚）
        """
        num_lines = self.line_density
        for i in range(num_lines):
            angle = 2 * math.pi * i / num_lines
            line_points = self._trace_field_line(source.x, source.y, angle, outward=False)
            if line_points:
                self.field_lines.append(line_points)
    
    def _trace_field_line(self, start_x, start_y, angle, outward=True):
        """
        追踪单条电场线 - 使用正确的电场强度计算
        :param start_x: 起始x坐标
        :param start_y: 起始y坐标
        :param angle: 起始角度
        :param outward: 是否向外（正电荷True，负电荷False）
        :return: 电场线上的点列表
        """
        points = []
        x, y = start_x, start_y
        step_size = performance_settings.field_line_step_size
        max_steps = int(performance_settings.max_field_line_length // step_size)
        
        # 从电荷源稍微偏移一点开始，避免数值问题
        offset = 15
        x += math.cos(angle) * offset
        y += math.sin(angle) * offset
        
        for step in range(max_steps):
            # 检查是否超出屏幕边界
            if x < 0 or x > WIDTH or y < 0 or y > HEIGHT:
                break
                
            points.append((x, y))
            
            # 计算当前位置的电场强度
            total_ex, total_ey = 0, 0
            for source in self.sources:
                dx = x - source.x
                dy = y - source.y
                distance = math.hypot(dx, dy)
                
                # 避免除零错误
                if distance < 1:
                    continue
                    
                # 计算电场强度（使用库仑定律）
                e_strength = config.k * abs(source.equal) / (distance ** 2)
                
                # 计算单位向量
                unit_x = dx / distance
                unit_y = dy / distance
                
                # 根据电荷正负确定电场方向
                if source.equal > 0:  # 正电荷
                    total_ex += e_strength * unit_x
                    total_ey += e_strength * unit_y
                else:  # 负电荷
                    total_ex -= e_strength * unit_x
                    total_ey -= e_strength * unit_y
            
            # 如果电场强度太小，停止追踪
            field_magnitude = math.hypot(total_ex, total_ey)
            if field_magnitude < 1e-3:
                break
                
            # 计算电场方向
            if outward:
                # 正电荷：沿电场方向
                direction_x = total_ex / field_magnitude
                direction_y = total_ey / field_magnitude
            else:
                # 负电荷：沿电场反方向
                direction_x = -total_ex / field_magnitude
                direction_y = -total_ey / field_magnitude
            
            # 沿电场方向移动
            x += direction_x * step_size
            y += direction_y * step_size
            
            # 检查是否太接近其他电荷源
            too_close = False
            for source in self.sources:
                if math.hypot(x - source.x, y - source.y) < 15:
                    too_close = True
                    break
            if too_close:
                break
        
        return points
    
    def draw_field_lines(self, screen):
        """
        绘制电场线
        """
        if not self.show_field_lines:
            return
        
            
        for line_points in self.field_lines:
            if len(line_points) > 1:
                # 绘制平滑的电场线 - 使用更明显的颜色和更粗的线条
                pygame.draw.lines(screen, (0, 255, 255), False, line_points, 2)  # 青色，2像素宽
                
                # 根据性能设置决定是否绘制箭头
                if performance_settings.draw_arrows:
                    self._draw_arrows(screen, line_points)
    
    def _draw_arrows(self, screen, points):
        """
        在电场线上绘制箭头
        """
        arrow_spacing = performance_settings.arrow_spacing  # 使用性能设置中的箭头间距
        arrow_size = 3
        
        for i in range(arrow_spacing, len(points) - arrow_spacing, arrow_spacing):
            if i + 1 < len(points):
                x1, y1 = points[i]
                x2, y2 = points[i + 1]
                
                # 计算箭头方向
                dx = x2 - x1
                dy = y2 - y1
                length = math.hypot(dx, dy)
                
                if length > 0:
                    # 归一化方向向量
                    dx /= length
                    dy /= length
                    
                    # 计算箭头顶点
                    arrow_x1 = x1 + dx * arrow_size
                    arrow_y1 = y1 + dy * arrow_size
                    arrow_x2 = x1 + dx * arrow_size * 0.5 + dy * arrow_size * 0.5
                    arrow_y2 = y1 + dy * arrow_size * 0.5 - dx * arrow_size * 0.5
                    arrow_x3 = x1 + dx * arrow_size * 0.5 - dy * arrow_size * 0.5
                    arrow_y3 = y1 + dy * arrow_size * 0.5 + dx * arrow_size * 0.5
                    
                    # 绘制箭头 - 使用更明显的颜色
                    pygame.draw.polygon(screen, (0, 255, 255), 
                                      [(arrow_x1, arrow_y1), (arrow_x2, arrow_y2), (arrow_x3, arrow_y3)])
# 电场源创建类，集成动画
class CreateElectricField:
    def __init__(self,x,y,w,h):
        self.x = x
        self.origin_x = x  # 保存初始位置
        self.y = y
        self.w = w
        self.h = h
        self.is_animating = False  # 动画状态标志
        self.animation_speed = 4   # 动画速度
        self.current_charge = 10   # 当前电荷量
    
    def start_animation(self):
        """开始动画"""
        self.is_animating = True
        self.x = self.origin_x  # 重置到初始位置
    
    def update(self):
        """更新动画"""
        if self.is_animating and self.x > 650:
            self.x -= self.animation_speed
        elif self.is_animating and self.x <= 650:
            # 动画结束
            self.is_animating = False
    
    def draw(self):
        """绘制创建框"""
        global button_createElectric, create_text_input_x, create_text_input_y, create_slider, button_confirm_create
        if showCreateElectric:
            pygame.draw.rect(screen, WHITE, (self.x, self.y, self.w, self.h), 2, 6)
            # 添加一些文字说明
            text_ch.displayText(self.x + 5, self.y + 6, "创建电场源", BLUE)
            
            # 1. X坐标输入框
            text_sm.displayText(self.x + 10, self.y + 30, "X坐标:(默认400)")
            if create_text_input_x is None:
                create_text_input_x = textEntry(self.x + 10, self.y + 50, 80, 25)
            else:
                create_text_input_x.set_relative_position((self.x + 10, self.y + 50))
            
            # 2. Y坐标输入框
            text_sm.displayText(self.x + 10, self.y + 80, "Y坐标:(默认300)")
            if create_text_input_y is None:
                create_text_input_y = textEntry(self.x + 10, self.y + 100, 80, 25)
            else:
                create_text_input_y.set_relative_position((self.x + 10, self.y + 100))
            
            # 3. 电荷量滑块
            text_sm.displayText(self.x + 10, self.y + 130, "电荷量:")
            if create_slider is None:
                create_slider = pygame_gui.elements.UIHorizontalSlider(
                    relative_rect=pygame.Rect(self.x + 10, self.y + 150, 160, 25),
                    start_value=10,
                    value_range=(-50, 50),
                    manager=manager,
                    click_increment=1
                )
            else:
                create_slider.set_relative_position((self.x + 10, self.y + 150))
            
            # 显示当前电荷量
            charge_text = f"当前值: {self.current_charge}"
            charge_color = (100, 255, 100) if self.current_charge > 0 else (255, 100, 100) if self.current_charge < 0 else (255, 255, 255)
            text_sm.displayText(self.x + 10, self.y + 175, charge_text, charge_color)
            
            # 4. 确认创建按钮
            button_width = 60
            button_x = self.x + (self.w - button_width) // 2  # 水平居中
            if button_confirm_create is None:
                button_confirm_create = buttons(button_x, self.y + 195, button_width, 26, "创建", 'create new electric field source')
            else:
                button_confirm_create.set_relative_position((button_x, self.y + 195))
        else:
            # 当面板隐藏时，销毁所有UI组件
            if button_createElectric is not None:
                button_createElectric.kill()
                button_createElectric = None
            if create_text_input_x is not None:
                create_text_input_x.kill()
                create_text_input_x = None
            if create_text_input_y is not None:
                create_text_input_y.kill()
                create_text_input_y = None
            if create_slider is not None:
                create_slider.kill()
                create_slider = None
            if button_confirm_create is not None:
                button_confirm_create.kill()
                button_confirm_create = None

CreateBox = CreateElectricField(680,100,180,240)
# 创建电场源
arr = createElectricSourceArrB(config.SourcePosArr)
# 创建电场线系统
field_lines_system = ElectricFieldLines(arr)
field_lines_system.calculate_field_lines()
def on_test_click():
    global index
    if 0 <= index < len(arr):
        arr[index].start_delete()  # 开始动画

# 出现随机值让小球变得更平均
thetas = random.randint(0,120)/180 * math.pi
theta = random.randint(120,240)/180 * math.pi
thetat = random.randint(240,360)/180 * math.pi
# 创建文本的对象
test = Text()
pos_text = Text()
fps_text = Text()  # 帧率显示文本
# 创建一个动画的对象
ani_obj = Animation(680,35)

# 轨迹点队列，最大保存1000个点
trajectory_points = collections.deque(maxlen=1000)
# 存储放置电场源的双击参数
last_tickS = 0
# 间隔时间300毫秒

# 帧率计算
fps_counter = 0
fps_timer = 0
current_fps = 0
intervalS = 300
deltaS = 0
# 存储提取电场源你的双击参数
last_tickU = 0
# 间隔时间300毫秒
intervalU = 300
deltaU = 0
index = -1
# 保存被选择的电场源对象，便于操作
save_obj = 0
rightOnclick = False
# 检测是被双击选中
select = False
running = True

# 显示初始性能模式
show_initial_performance_mode()
while running:
    time_delta = clock.tick(performance_settings.target_fps)  # 使用性能设置中的目标帧率
    screen.fill((0, 0, 0))
    # if popup_visible:
    #     introduce()
    CreateBox.update()
    CreateBox.draw()
    particle.displayParticleData(650,390,180,80)
    # 获取光标位置坐标
    pos = pygame.mouse.get_pos()
    pos_text.displayText(10,HEIGHT-20,f"Position: ({pos[0]}, {pos[1]})")
    
    # 计算和显示帧率
    fps_counter += 1
    fps_timer += time_delta
    if fps_timer >= 1000:  # 每秒更新一次帧率显示
        current_fps = fps_counter
        fps_counter = 0
        fps_timer = 0
    fps_text.displayText(10, HEIGHT-40, f"FPS: {current_fps}")
    # 绘制右键按钮
    if rightOnclick:
        butt = Button(pos[0] + 20, pos[1] + 20, 25, 25, WHITE, 14, RED, "img/right.png", "img/right_blue.png", slide_right,
                  "创建电场源")
        butt.normalDraw(pos)
    
    # 绘制电场线按钮
    but_field_lines.normalDraw(pos)
    but_field_create.normalDraw(pos)
    but_performance.normalDraw(pos)
    # 如果不在滑动中,就绘制右滑按钮
    # 正确设置左滑按钮和右滑按钮
    # 条件解释:不在滑动状态且也不在隐藏状态
    if not ParticleDataSlideToHide and not ParticleDataSlideToShow and not ani_p.hide_state:
        but_right.rect = but_right.saveRect
        but_right.imgDraw(pos,screen)
        but_left.rect = pygame.Rect(0,0,0,0)
    # 条件解释：不在左滑状态并且在隐藏状态
    if not ParticleDataSlideToShow and ani_p.hide_state == True:
        but_left.rect = but_left.saveRect
        but_left.imgDraw(pos,screen)
        but_right.rect = pygame.Rect(0,0,0,0)
    # 如果被双击选中，就跟随鼠标光标
    if select:
        save_obj.x = pos[0]
        save_obj.y = pos[1]
    # 遍历场源电荷,判断是否被选中
    # 收集所有按钮对象
    button_list = [but_right,but_left,but_field_lines,but_field_create,but_performance,but_cancel_introduce]
    # 计算被选中场源电荷的索引值
    ind = 0
    for i in arr:
        # 当双击大于300毫秒就播放动画，不然会误播放两次
        if i.isHovered and deltaU>300:
            btn = i.displayElectricFieldData(screen, ani_obj, pos)
            for j in btn:
                button_list.append(j)
            break
        ind += 1
    if index != ind:
        index = ind
        if text_input is not None:
            text_input.kill()  # 销毁旧输入框
            text_input = None
        if slider is not None:
            slider.kill()
            slider = None
        if button_SetPos is not None:
            button_SetPos.kill()
            button_SetPos = None
        if button_copyElectric is not None:
            button_copyElectric.kill()
            button_copyElectric = None
        if button_deleteElectric is not None:
            button_deleteElectric.kill()
            button_deleteElectric = None
        if button_createElectric is not None:
            button_createElectric.kill()
            button_createElectric = None
    # 绘制场源电荷的所有样式和方法，用于优化代码重复调用
    drawSourceTotal(arr)
    
    # 绘制电场线（优化版本）
    if field_lines_system is not None:
        # 只在电场源发生变化时重新计算电场线
        if field_lines_system.should_recalculate():
            field_lines_system.calculate_field_lines()
        field_lines_system.draw_field_lines(screen)
        
    # 创建一个电场系统对象，用于计算粒子受到的电场力（优化版本）
    if 0<=body.position.x<=WIDTH and 0<=body.position.y<=HEIGHT:
        # 只在粒子在屏幕内时计算电场力
        ElectricField = ElectricFieldSystem(body.position[0],body.position[1],arr)
        count = ElectricField.countElectricField()
        
        # 计算粒子受到的力：F = q * E
        force_scale = 1e-6  # 增大缩放因子,使粒子能被观察到
        force_x = particle.equal * count[0] * force_scale
        force_y = particle.equal * count[1] * force_scale
        
        # 限制最大力的大小
        max_force = 1000
        force_magnitude = math.sqrt(force_x**2 + force_y**2)
        if force_magnitude > max_force:
            force_x = force_x * max_force / force_magnitude
            force_y = force_y * max_force / force_magnitude
    else:
        # 粒子在屏幕外时不计算电场力
        force_x = 0
        force_y = 0


    # 对小球施加力
    body.apply_force_at_local_point((force_x, force_y), (0,0))

    # 记录轨迹点
    trajectory_points.append((body.position.x, body.position.y))

    # 添加边界检测，防止粒子飞出屏幕
    if body.position.x < 0 or body.position.x > WIDTH or body.position.y < 0 or body.position.y > HEIGHT:
        body.position = (startX, startY)  # 重置位置
        body.velocity = (0, 0)      # 重置速度
        trajectory_points.clear()   # 出界时清空轨迹

    space.debug_draw(draw_options)
    # 绘制平滑运动轨迹
    draw_smooth_trajectory(trajectory_points)
    # 在pymunk调试绘制之后添加额外的粒子绘制，这样不会被覆盖


    # # 绘制粒子的尾巴
    # particle.drawTrajectory()
    # config.count += 1
    # # 使模拟小球的尾巴喷气粒子数少于30个
    # if config.count>30:
    #     space.remove(config.bodyAndShape[config.count-30][0],config.bodyAndShape[config.count-30][1])

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
            break
        elif event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 1:  # 左键点击
                # 在弹窗显示时，只处理取消按钮的点击
                if popup_visible:
                    if but_cancel_introduce.rect.collidepoint(event.pos):
                        but_cancel_introduce.callback()
                else:
                    # 鼠标双击检测事件检测(放置)
                    now = pygame.time.get_ticks()
                    deltaS = now-last_tickS
                    if deltaS<=intervalS and select == True:
                        last_tickS = 0
                        n = len(arr)
                        for i in range(n):
                            if save_obj == arr[i]:
                                # 更改放置电场源后的数据
                                config.SourcePosArr[i][0],config.SourcePosArr[i][1] = pos[0],pos[1]
                                arr[i].x,arr[i].y = pos[0],pos[1]
                                arr[i].rect = pygame.Rect(pos[0]-25,pos[1]-25,50,50)
                        select=False
                    else:
                        last_tickS=now
                    # 检查所有按钮是否被点击，使用collidepoint直接检测点击位置
                    for btn in button_list:
                        if btn is not None:
                            if btn.rect.collidepoint(event.pos):
                                btn.callback()
                                break
                # 创建功能区矩形以检测点击位置
                    function_area_rect = pygame.Rect(610, 35, 240, 455)
                # 检查点击是否在功能区内
                    if function_area_rect.collidepoint(pos):
                    # 如果点击在功能区内，保留当前选中状态且不重置动画
                        pass  # 不执行任何操作，保留isHovered状态和当前动画状态
                    else:
                    # 如果点击不在功能区内，重置动画并正常判断选中状态
                        ani_obj.reset()
                        for i in arr:
                            i.JudgeHovered(pos)
                        # 鼠标双击检测事件（提取）
                            if i.isHovered:
                                now = pygame.time.get_ticks()
                                deltaU = now - last_tickU
                                if deltaU<=intervalU:
                                    last_tickU = 0
                                    save_obj = i
                                    select = True
                                    i.rect = pygame.Rect(0,0,10,10)
                                else:
                                    last_tickU = now
            elif event.button == 3:
                message_system.add_message("进入程序介绍页面", (255, 255, 100))
                # 销毁所有GUI组件
                destroy_all_gui_components()
                popup_visible = True
                # rightOnclick = True
        manager.process_events(event)
        if event.type == pygame_gui.UI_TEXT_ENTRY_FINISHED:
            if event.ui_element == text_input:
                text = event.text
                vis = [int(x) for x in text[1:][:-1].split(",")]
                config.SourcePosArr[index][0] = vis[0]
                config.SourcePosArr[index][1] = vis[1]
                # 同步更新arr里的对象坐标
                arr[index].x = vis[0]
                arr[index].y = vis[1]
                arr[index].rect.x = vis[0] - 25
                arr[index].rect.y = vis[1] - 25
            # 处理创建面板的输入框（这里不需要特殊处理，值会在点击创建按钮时获取）
        if event.type == pygame_gui.UI_HORIZONTAL_SLIDER_MOVED and event.ui_element == slider:
            value = slider.get_current_value()
            arr[index].equal = value
            config.SourcePosArr[index][2] = value
            # 更新电场线
            if field_lines_system is not None:
                field_lines_system.sources = arr
                field_lines_system.calculate_field_lines()
        # 处理创建面板的滑块移动
        if event.type == pygame_gui.UI_HORIZONTAL_SLIDER_MOVED and event.ui_element == create_slider:
            CreateBox.current_charge = create_slider.get_current_value()
        if event.type == pygame_gui.UI_BUTTON_PRESSED:
            if event.ui_element == button_SetPos:
                on_set_click()
            if event.ui_element == button_deleteElectric:
                on_test_click()
            if event.ui_element == button_copyElectric:
                config.SourcePosArr.append([config.SourcePosArr[index][0]+100,config.SourcePosArr[index][1],config.SourcePosArr[index][2]])
                arr = createElectricSourceArrB(config.SourcePosArr)
                # 更新电场线
                if field_lines_system is not None:
                    field_lines_system.sources = arr
                    field_lines_system.calculate_field_lines()
            if event.ui_element == button_confirm_create:
                on_confirm_create_click()

    manager.update(time_delta/1000.0)
    manager.draw_ui(screen)
    
    # 绘制弹窗（在GUI组件之后绘制，确保覆盖）
    if popup_visible:
        introduce(pos)
    
    # 绘制消息系统
    message_system.draw(screen)
    
    dt = 1.0 / 60.0
    space.step(dt)
    pygame.display.flip()
    # clock.tick(60)
    # 主循环中，删除动画播放完再真正删除
    for i in reversed(range(len(arr))):
        src = arr[i]
        if src.is_deleting and src.delete_progress >= 1:
            del arr[i]
            del config.SourcePosArr[i]
            # 如果删除的是当前选中的电场源，销毁输入框
            if text_input is not None and i == index:
                text_input.kill()
                text_input = None
            if slider is not None and i == index:
                slider.kill()
                slider = None
            if button_SetPos is not None and i == index:
                button_SetPos.kill()
                button_SetPos = None
            if button_copyElectric is not None and i == index:
                button_copyElectric.kill()
                button_copyElectric = None
            if button_deleteElectric is not None and i==index:
                button_deleteElectric.kill()
                button_deleteElectric = None
            if button_createElectric is not None and i == index:
                button_createElectric.kill()
                button_createElectric = None
            # 如果删除的是第一个或最后一个，index 需要修正
            if i <= index:
                index -= 1
            # 重新计算电场线
            if field_lines_system is not None:
                field_lines_system.sources = arr
                field_lines_system.calculate_field_lines()
pygame.quit()
sys.exit()

